import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:star_author/config/api.dart';
import 'package:star_author/config/app.dart';
import 'package:star_author/provider/index.dart';
import 'package:star_author/utils/common_util/log_util.dart';
import 'package:star_author/utils/common_util/shared_preferences/sp_util.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:video_thumbnail/video_thumbnail.dart';

import 'common_util/date_util.dart';
import 'common_util/num_util.dart';

class GetRect {
  late double _positionX, _positionY, _width, _height;

  double get positionX => _positionX;

  double get positionY => _positionY;

  double get width => _width;

  double get height => _height;

  GetRect({required GlobalKey containerKey, required GlobalKey targetKey}) {
    GetSize size = GetSize(key: targetKey);
    this._width = size.width;
    this._height = size.height;

    GetPosition containerPosition = GetPosition(key: containerKey);
    GetPosition targetPosition = GetPosition(key: targetKey);

    this._positionX = targetPosition.x - containerPosition.x;
    this._positionY = targetPosition.y - containerPosition.y;
  }
}

class GetRenderBox {
  late RenderBox? _box;

  RenderBox? get box => _box;

  GetRenderBox({required GlobalKey key}) {
    BuildContext? ctx = key.currentContext;
    _box = ctx!.findRenderObject() as RenderBox;
  }
}

class GetPosition {
  late double _x, _y;

  double get x => _x;

  double get y => _y;

  GetPosition({required GlobalKey key}) {
    RenderBox? box = GetRenderBox(key: key).box;
    final position = box!.localToGlobal(Offset.zero);
    _x = position.dx;
    _y = position.dy;
  }
}

class GetSize {
  late double _width, _height;

  double get width => _width;

  double get height => _height;

  GetSize({required GlobalKey key}) {
    RenderBox? box = GetRenderBox(key: key).box;
    Size size = box!.size;
    _width = size.width;
    _height = size.height;
  }
}

Function debounce({
  required Function callback,
  required Duration delay,
}) {
  Timer? timer;
  bool first = true;
  return ([List? args]) {
    if (first) {
      if (args != null) {
        callback(args);
      } else {
        callback();
      }
      first = false;
    }
    if (timer != null) {
      timer!.cancel();
    }
    timer = Timer(delay, () {
      if (args != null) {
        callback(args);
      } else {
        callback();
      }
      timer = null;
    });
  };
}

Function() throttle({
  required Function callback,
  required Duration delay,
}) {
  bool first = true;
  bool vaild = true;
  return ([List? args]) {
    if (first) {
      if (args != null) {
        callback(args);
      } else {
        callback();
      }
      first = false;
    }
    if (vaild) {
      vaild = false;
      Timer(delay, () {
        if (args != null) {
          callback(args);
        } else {
          callback();
        }
        vaild = true;
      });
    }
  };
}

class GetRadius {
  static Radius _getRadius(double? deg) {
    return Radius.circular(deg ?? 0);
  }

  static BorderRadius only({
    double? topLeft,
    double? topRight,
    double? bottomLeft,
    double? bottomRight,
  }) {
    return BorderRadius.only(
      topLeft: _getRadius(topLeft),
      topRight: _getRadius(topRight),
      bottomLeft: _getRadius(bottomLeft),
      bottomRight: _getRadius(bottomRight),
    );
  }

  static BorderRadius all(double deg) {
    return BorderRadius.all(_getRadius(deg));
  }

  static BorderRadius circle() {
    return BorderRadius.circular(9999);
  }
}

class GetPadding {
  static only({
    double? left,
    double? top,
    double? right,
    double? bottom,
  }) {
    return EdgeInsets.fromLTRB(
      left ?? 0,
      top ?? 0,
      right ?? 0,
      bottom ?? 0,
    );
  }

  static all(double value) {
    return EdgeInsets.all(value);
  }
}

class GetTimer {
  static Map<String, Map<String, Timer>> _timerCache = {};
  static Map<String, Map<String, Function(Timer)>> _cbCache = {};
  static List<bool> _isPause = [false];

  static bool get isPause => _isPause[0];

  static Map<String, Map<String, Timer>> get timerCache => _timerCache;

  static Map<String, Map<String, Function(Timer)>> get cbCache => _cbCache;

  /// 根据传入的[name]生成的[key]来确定对应的回调方法列表，
  /// 该方法会返回一个[key]，用于在[remove]方法中注销回调
  static String add({
    required String name,
    required int millisec,
    required Function(Timer) callback,
    bool? immediate = false,
  }) {
    String key = name + new DateTime.now().millisecondsSinceEpoch.toString();
    Timer periodic = Timer.periodic(
      Duration(milliseconds: millisec),
      (timer) {
        if (!_isPause[0]) {
          if (checkNotNullOrNotEmpty(_cbCache[name])) {
            _cbCache[name]!.forEach((key, callback) {
              callback(timer);
            });
          }
        }
      },
    );
    if (immediate!) {
      callback(periodic);
    }
    if (_timerCache.containsKey(name) && _cbCache.containsKey(name)) {
      _timerCache[name]![key] = periodic;
      _cbCache[name]![key] = callback;
    } else {
      _timerCache[name] = {key: periodic};
      _cbCache[name] = {key: callback};
    }
    return key;
  }

  /// 同一任务名称[name]共享同一任务队列，任务队列中根据[key]对应回调函数
  static remove({
    required String name,
    required String key,
  }) {
    Map<String, Function(Timer)>? cbMap = _cbCache[name];
    Map<String, Timer>? timerMap = _timerCache[name];
    if (timerMap != null) {
      timerMap[key]!.cancel();
      timerMap.remove(key);
    }
    if (cbMap != null) {
      cbMap.remove(key);
      if (cbMap.length == 0) {
        _cbCache.remove(name);
        _timerCache.remove(name);
      }
    }
  }

  static clear() {
    _timerCache.forEach((name, timerMap) {
      timerMap.forEach((key, timer) {
        timer.cancel();
      });
    });
    _timerCache = {};
    _cbCache = {};
  }

  static start() {
    _isPause[0] = false;
  }

  static pause() {
    _isPause[0] = true;
  }
}

/// 处理金额显示的方法，里面逻辑待完善，现项目中所有金额统一用这个方法处理
String formatPrice(price, {bool convert = false}) {
  String _price;

  if (price is int || price is double) {
    _price = price.toString();
  } else {
    if (price == null) price = '';

    String defaultValue = convert ? '0.0' : price;

    double? doubleValue = double.tryParse(price);

    if (doubleValue == null) {
      _price = defaultValue;
    } else {
      if (doubleValue == doubleValue.round()) {
        // 整型的时候，不要后面的".0"
        _price = doubleValue.round().toString();
      } else {
        _price = doubleValue.toStringAsFixed(2);
      }
    }
  }

  return _price;
}

num parseNum(value) {
  if ([int, double].contains(value.runtimeType)) {
    return value;
  } else if (value is String) {
    return num.parse(value);
  } else {
    return 0;
  }
}

String parseString(value) {
  if ([int, double].contains(value.runtimeType)) {
    return value.toString();
  } else if (value is String) {
    return value;
  } else {
    return '';
  }
}

bool checkStringNullOrEmpty(String? value) {
  return value == null || value.isEmpty || value == "";
}

bool checkNotNullOrNotEmpty(dynamic value) {
  if (value == null) return false;
  if (value is String) return !checkStringNullOrEmpty(value);
  return true;
}

getImmediatePrice(deductRate, price) {
  if (price.toString().contains('*')) {
    //*表示竞拍中 直接返回
    return price;
  }
  price = double.tryParse(price ?? "0");
  deductRate = double.tryParse(deductRate);
  return price != null && price != 0
      ? NumUtil.add(price, -NumUtil.multiply(price, deductRate))
      : price;
}

getUserInfo(BuildContext context) async {
  var res = await API.getUserInfo();
  var data = res.data;
  //Get.context! 全局通用上下文 不能使用传入的上下文 会出现无法刷新的问题
  Provider.of<UserProvider>(Get.context!, listen: false)
      .set(key: 'userInfo', value: data);

  LogUtil.v(
      "极光推送设置App角标数量 为 ${data["summary"]["unread_notifications_count"] ?? 0}");
}

void checkNetwork() async {
  var connectivityResult = await (Connectivity().checkConnectivity());
  if (connectivityResult == ConnectivityResult.mobile) {
    // EasyLoading.showToast('移动网络');
  } else if (connectivityResult == ConnectivityResult.wifi) {
    // EasyLoading.showToast('wifi网络');
  } else {
    EasyLoading.showToast('无网络，请检查网络链接');
  }
}

//将字符串切割成金额样式  比如1000000转成1.000.000  或  200000转成200.000
//也可以将所有的.替换成,   这样就是以,分隔 比如1,000,000或者200,000
// ignore: missing_return
String getMoneyStyleStr(String text) {
  try {
    if (text == null || text.isEmpty) {
      return "";
    } else {
      String temp = "";
      if (text.length <= 3) {
        temp = text;
        return temp;
      } else {
        int count = ((text.length) ~/ 3); //切割次数
        int startIndex = text.length % 3; //开始切割的位置
        if (startIndex != 0) {
          if (count == 1) {
            temp = text.substring(0, startIndex) +
                "," +
                text.substring(startIndex, text.length);
          } else {
            temp = text.substring(0, startIndex) + ","; //第一次切割0-startIndex
            int syCount = count - 1; //剩余切割次数
            for (int i = 0; i < syCount; i++) {
              temp +=
                  text.substring(startIndex + 3 * i, startIndex + (i * 3) + 3) +
                      ",";
            }
            temp += text.substring(
                (startIndex + (syCount - 1) * 3 + 3), text.length);
          }
        } else {
          for (int i = 0; i < count; i++) {
            if (i != count - 1) {
              temp += text.substring(3 * i, (i + 1) * 3) + ",";
            } else {
              temp += text.substring(3 * i, (i + 1) * 3);
            }
          }
        }
        return temp;
      }
    }
  } catch (e) {
    LogUtil.v(e);
  }
  return "";
}

///todo 阿里云免费银行卡校验
Future<bool> checkBank(cardNo) async {
  String url =
      "https://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8&cardNo=";
  url += cardNo;
  url += "&cardBinCheck=true";
  bool validated = false;
  await Dio().get(url).then((value) => validated = value.data['validated']);
  return validated;
}

//todo:时间转换  本地日期
String formatDate(date, {format: "yyyy-MM-dd HH:mm:ss"}) {
  return DateUtil.formatDateStr(
    date,
    isUtc: false,
    format: format,
  );
}

String ObfuscateString(String text, {int? start: 0, int? end}) {
  int _end = end ?? text.length;
  List<String> list = text.split('');
  [...list].asMap().forEach((index, char) {
    if (index >= start! && index <= _end) {
      list[index] = '*';
    }
  });
  return list.join('');
}

num _getPow(num numa, num numb) {
  num r1, r2;
  r1 = numa.toString().split('.')[1].length;
  r2 = numb.toString().split('.')[1].length;
  return pow(10, max(r1, r2));
}

//size.substring(0,size.indexOf(".")+3) 小数点位数
String formatFileSize(int fileSize, {int fractionDigits = 2}) {
  if (fileSize <= 0) return '0 B';
  int unit = 1024;
  if (Platform.isIOS) {
    unit = 1000;
  }
  if (fileSize > unit * unit * unit) {
    return '${(fileSize / unit / unit / unit).toStringAsFixed(fractionDigits)} GB';
  } else if (fileSize > unit * unit) {
    return '${(fileSize / unit / unit).toStringAsFixed(fractionDigits)} MB';
  } else if (fileSize > unit) {
    return '${(fileSize / unit).toStringAsFixed(fractionDigits)} KB';
  } else {
    return '$fileSize B';
  }
}

// 版本号对比
bool compareVersion(curV, reqV) {
  if (curV != null && reqV != null) {
    //将两个版本号拆成数字
    var arr1 = curV.split('.'), arr2 = reqV.split('.');
    int arr1_len = arr1.length;
    int arr2_len = arr2.length;
    int minLength = min(arr1_len, arr2_len), position = 0, diff = 0;
    // 依次比较版本号每一位大小，当对比得出结果后跳出循环（后文有简单介绍）
    while (position < minLength &&
        ((diff = int.parse(arr1[position]) - int.parse(arr2[position])) == 0)) {
      position++;
    }
    diff = (diff != 0) ? diff : (arr1.length - arr2.length);
    //若curV大于reqV，则返回true
    return diff > 0;
  } else {
    //输入为空
    LogUtil.v("版本号不能为空");
    return false;
  }
}

num floatMultiplication(num numa, num numb) {
  num pow = _getPow(numa, numb);
  num _a = numa * pow;
  num _b = numb * pow;
  return (_a * _b).truncate() / (pow * pow);
}
// String CompleteNumber(num number) {
//   if (number < 10) {
//     return '0' + number.toString();
//   } else {
//     return number.toString();
//   }
// }

//颜色转换
Color hexToColor(String hexString, {String alphaChannel = 'FF'}) {
  return Color(int.parse(hexString.replaceFirst('#', '0x$alphaChannel')));
}

Color rgba(r, g, b, double opacity) {
  return Color.fromRGBO(r, g, b, opacity);
}

Color RGBA(r, g, b, double opacity) {
  return Color.fromRGBO(r, g, b, opacity);
}

//todo:创建回流单预览图拓展
String getBackFlowPreviewTitle(String suffix) {
  if (suffix == "P") {
    return "画作照片";
  }
  return '货品总览图';
}

//todo:将昵称转换为名称中间带*号的 李白没我白 to 李*白
String getNickNameWithStart(String nickname) {
  String result = "";
  //截取需要用runes来 不然会报错
  if (nickname.length > 0) {
    String first = String.fromCharCode(nickname.runes.first);
    String last = String.fromCharCode(nickname.runes.last);
    result = first + "*" + last;
  }
  return result;
}

//todo:讲昵称转换为开头为昵称 后面所有文字为** 如： 李白没我白 to 李**
String getNickNameWithContentStart(String nickname) {
  String result = "";
  //截取需要用runes来 不然会报错
  if (nickname.length > 0) {
    String first = String.fromCharCode(nickname.runes.first);
    result = first + "**";
  }
  return result;
}

//根据参数返回需要的painter 用于文字的行数判断
TextPainter getTextPainter(BuildContext context, String text, TextStyle style,
    double maxWidth, int maxLines) {
  double textScaleFactor = MediaQuery.of(context).textScaleFactor;
  final TextPainter textPainter = TextPainter(
      locale: WidgetsBinding.instance!.window.locale,
      text: TextSpan(text: text, style: style),
      textScaleFactor: textScaleFactor,
      maxLines: maxLines,
      textDirection: TextDirection.ltr)
    ..layout(minWidth: 0, maxWidth: maxWidth);
  return textPainter;
}

//根据数组返回金额范围
String getPriceWithList(
  List list, {
  bool intl: false,
  String show_name: "",
}) {
  String min = "0";
  String max = "0";
  //有show_name就展示show_name，没有show_name，再展示价格区间
  if (show_name.isNotEmpty) {
    return show_name;
  }
  if (list.length == 0) {
    return "";
  }
  if (list.length == 1) {
    min = list[0].toString();
    max = list[0].toString();
  }
  if (list.length >= 2) {
    min = list[0].toString();
    max = list[1].toString();
  }
  if (max == min) {
    return "该货品无参考估价";
  }
  if (intl) {
    //浮点类型需要先double 然后int
    min = double.parse(min).toInt().toString();
    max = double.parse(max).toInt().toString();
    return "¥${getMoneyStyleStr(min)}-¥${getMoneyStyleStr(max)}";
  }
  return "¥$min-¥$max";
}

//生成距离现在日期过去多少天/小时/分
String getDateStr(String time, {String format: "距离现在过去"}) {
  Duration duration =
      DateTime.now().difference(DateUtil.getDateTime(time) ?? DateTime.now());
  String result = format;
  if (duration.inDays <= 0) {
    if (duration.inHours <= 0) {
      if (duration.inMinutes <= 0) {
        result += duration.inSeconds.toString() + "秒";
      } else {
        result += duration.inMinutes.toString() + "分";
      }
    } else {
      result += duration.inHours.toString() + "小时";
    }
  } else {
    result += duration.inDays.toString() + "天";
  }
  return result;
}

//获取视频预览图 支持http 和本地路径
Future<Uint8List> getThumbnail(String videoPath, int width, int height) async {
  Uint8List? uint8list = await VideoThumbnail.thumbnailData(
    video: videoPath,
    imageFormat: ImageFormat.JPEG,
    maxWidth: width,
    quality: 100,
    maxHeight: height,
  );
  return uint8list ?? Uint8List(0);
}

//获取本地资源的文件
Future<Uint8List> getAssetBytes(String assetPath) async {
  final byteData = await rootBundle.load(assetPath);
  return byteData.buffer.asUint8List();
}

// 压缩图片
Future<Uint8List> compressImage(
  Uint8List uint8list, {
  int minWidth: 375,
  int minHeight: 256,
  int quality: 94,
}) async {
  var result = await FlutterImageCompress.compressWithList(
    uint8list,
    minWidth: minWidth,
    minHeight: minHeight,
    quality: quality,
  );
  return result;
}

//获取截图数据
Future<Uint8List> getScreenshot(GlobalKey key) async {
  RenderRepaintBoundary boundary =
      key.currentContext!.findRenderObject() as RenderRepaintBoundary;
  ui.Image image = await boundary.toImage(pixelRatio: 3.0);
  ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
  return byteData!.buffer.asUint8List();
}

//关注人数
String getStaredStr(int count) {
  String value = "";
  if (count.toString().length < 5) {
    return count.toString();
  }
  double div = NumUtil.divide(count, 10000);
  if (div < 10) {
    var num;
    if (div is double) {
      num = div;
    } else {
      num = double.parse(div.toString());
    }
    if ((num.toString().length - num.toString().lastIndexOf(".") - 1) < 2) {
      value = num.toStringAsFixed(2)
          .substring(0, num.toString().lastIndexOf(".") + 2 + 1)
          .toString();
    } else {
      value = num.toString()
          .substring(0, num.toString().lastIndexOf(".") + 2 + 1)
          .toString();
    }
  } else {
    value = div.toInt().toString();
  }
  return value + "万";
}

//清理弹幕消息本地缓存
clearEtList() {
  Map? local_et_messages = SpUtil.getObject(App.et_stream_key);
  if (local_et_messages?.isNotEmpty ?? false) {
    String local_et_messages_time =
        local_et_messages?['local_et_messages_time'] ?? "";
    if (local_et_messages_time.isNotEmpty) {
      //判断上一次点击的艾特消息的时间是否超过2个小时 超过就清除清除一下本地缓存
      DateTime? date_time = DateUtil.getDateTime(local_et_messages_time);
      if (date_time != null) {
        if (DateTime.now().difference(date_time).inHours > 2) {
          LogUtil.v("大于2小时");
          SpUtil.putObject(App.et_stream_key, {
            "list": [],
            "local_et_messages_time": DateUtil.getNowDateStr(),
          });
        } else {
          LogUtil.v("小于2小时");
        }
      }
    }
  }
}

String getRandomString(int len) {
  var r = Random();
  const _chars =
      'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
  return List.generate(len, (index) => _chars[r.nextInt(_chars.length)]).join();
}
